#include "CEventSystem.h"
#include "IListener.h"
#include "..\CGame.h"

//Functor to sort events by priority
bool EventPriority( CEvent& _cEvent1, CEvent& _cEvent2 )
{
	return _cEvent1.GetPriority(  ) > _cEvent2.GetPriority(  );
}

CEventSystem::CEventSystem(  ) 
{

}

CEventSystem::~CEventSystem(  ) 
{

}

void CEventSystem::DispatchEvent( CEvent* _pcEvent )
{
	if( m_mClients[_pcEvent->GetEventID(  )].empty(  ) )
	{
		return;
	}
	//Create an iterator to traverse the list of clients, registered for this event
	vector<IListener*>::iterator lisnItr = m_mClients[_pcEvent->GetEventID(  )].begin(  );

	//For every client, registered for this event, pass it the event
	for( ; lisnItr != m_mClients[_pcEvent->GetEventID(  )].end(  ); ++lisnItr )
	{
		( *lisnItr )->HandleEvent( _pcEvent );
	}
}

bool CEventSystem::AlreadyRegistered( EVENTID _nEventID, IListener* _pcClient )
{
	bool bAlreadyRegistered = false;
	//Create a iterator to traverse the Clients associated with this event
	vector<IListener*>::iterator lisnItr = m_mClients[_nEventID].begin(  );
	//If this event already knows about this client we are already registered
	for( ; lisnItr != m_mClients[_nEventID].end(  ); ++lisnItr )
	{
		if( _pcClient == ( *lisnItr ) )
		{	
			bAlreadyRegistered = true;
			break;
		}
	}
	//return the result
	return bAlreadyRegistered;
}

CEventSystem* CEventSystem::GetInstance( void )
{
	static CEventSystem instance;
	return &instance;
}

void CEventSystem::RegisterClient( EVENTID _nEventID, IListener* _pcClient )
{
	//Check if this is a valid Client, if not return, letting the caller know there was an error
	if( _pcClient == NULL  )
	{
		MessageBoxA( NULL, "Invalid Client Passed into EventSystems RegisterClient.", "ERROR", MB_OK );
		return;
	}

	//Check if this event has already been registered
	if( AlreadyRegistered( _nEventID, _pcClient ) )
	{
		return;
	}

	//If we've made it here push it onto the vector of clients for this event
	m_mClients[_nEventID].push_back( _pcClient );
	//let the client know what event there registered for
	_pcClient->m_vRegisteredEvents.push_back( _nEventID );
}

void CEventSystem::UnregisterClient( EVENTID _nEventID, IListener* _pcClient )
{
	//Create an iterator to traverse the clients this event is registered for
	vector<IListener*>::iterator lisnItr = m_mClients[_nEventID].begin(  );

	//Iterate the list, checking if we've founf our client
	for( ; lisnItr != m_mClients[_nEventID].end(  ); ++lisnItr )
	{
		if( _pcClient == ( *lisnItr ) )
		{	
			//remove the client and break, we're done here
			m_mClients[_nEventID].erase( lisnItr );
			break;
		}
	}

	//Create an iterator to traverse the clients list of registered events
	vector<int>::iterator evntItr = _pcClient->m_vRegisteredEvents.begin(  );

	//Iterate through that list and remove the EventID
	for( ; evntItr != _pcClient->m_vRegisteredEvents.end(  ); ++evntItr )
	{
		if( _nEventID == ( *evntItr ) )
		{
			_pcClient->m_vRegisteredEvents.erase( evntItr );
			break;
		}
	}
}

void CEventSystem::UnregisterClientAll( IListener* _pcClient )
{
	vector<int>::iterator evntItr = _pcClient->m_vRegisteredEvents.begin(  );

	//Create an iterator to traverse the clients this event is registered for
	vector<IListener*>::iterator lisnItr;

	for( ; evntItr != _pcClient->m_vRegisteredEvents.end(  ); ++evntItr )
	{
		vector<IListener*>::iterator lisnItr = m_mClients[*evntItr].begin(  );

		//Iterate the list, checking if we've found our client
		for( ; lisnItr != m_mClients[*evntItr].end(  ); ++lisnItr )
		{
			if( _pcClient == ( *lisnItr ) )
			{	
				//remove the client and break, we're done here
				m_mClients[*evntItr].erase( lisnItr );
				break;
			}
		}
	}

	_pcClient->m_vRegisteredEvents.clear(  );
}

void CEventSystem::SendEvent( EVENTID _nEventID, void* _pData, int _nPriority )
{
	if( _nPriority == EVNT_URGENT )
	{
		m_cUrgentEvent.SetEventID( _nEventID );
		m_cUrgentEvent.SetParam( _pData );
		DispatchEvent( &m_cUrgentEvent );
	}
	else
	{
		m_vCurrentEvents.push_back( CEvent( _nEventID, _pData, _nPriority ) );
	}
}

void CEventSystem::SendSafeEvent( EVENTID _nEventID, void* _pData, int _nPriority )
{
	if( _nPriority == EVNT_URGENT )
	{
		OutputDebugString( "SendSafeEvent cannot process an URGENT Event, returing from function." );
		return;
	}
	else
	{
		m_vSafeEvents.push_back( CEvent( _nEventID, _pData, _nPriority ) );
	}
}

void CEventSystem::ProcessEvents( void )
{
	//Push Back the safe events
	for ( unsigned int i = 0; i < m_vSafeEvents.size(  ); ++i )
	{
		m_vCurrentEvents.push_back( m_vSafeEvents[i] );
	}

	m_vSafeEvents.clear(  );

	sort( m_vCurrentEvents.begin(  ), m_vCurrentEvents.end(  ), EventPriority ); 

	vector<CEvent>::iterator itr = m_vCurrentEvents.begin(  );
	
	for (  ; itr != m_vCurrentEvents.end(  ); ++itr )
	{
		DispatchEvent( &( *itr ) );
	}

	ClearEvents(  );
}

void CEventSystem::ClearEvents( void )
{
	m_vCurrentEvents.clear(  );
}


void CEventSystem::Shutdown( void )
{
	m_mClients.clear(  );
	ClearEvents(  );
	m_vSafeEvents.clear(  );
}
